using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;


namespace ExtendedControls
{
	public enum ExtendedListViewMode
	{
		List,
		Tree
	}



	public partial class ExtendedListView : ListView
	{
		public event EventHandler DoHideChildren;
		public event EventHandler DoShowChildren;

		public int DepthOffset = 10;

		internal Guid ChildCheckStateRecalcGuid = new Guid();

		//		internal ExtendedListViewItemCollection Children;
		private ImageList secondaryImageList;

		public ImageList SecondaryImageList
		{
			get { return secondaryImageList; }
			set { secondaryImageList = value; }
		}

		private ExtendedListViewMode viewMode = ExtendedListViewMode.Tree;

		public ExtendedListViewMode ViewMode
		{
			get { return viewMode; }
			set { viewMode = value; }
		}


		public ExtendedListView()
		{
			InitializeComponent();
			_items = new ExtendedListViewItemCollection(this);
			this.DoubleBuffered = true;
			//			Children = new ExtendedListViewItemCollection(this);
		}

		ExtendedListViewItemCollection _items;

		public new ExtendedListViewItemCollection Items
		{
			get { return _items; }
		}

		public ListViewItemCollection BaseItems
		{
			get
			{
				return base.Items;
			}
		}

		internal void AddInternalItem(ExtendedListViewItem value)
		{
			base.Items.Add(value as ListViewItem);
			//if (value.ParentItem == null)
			//{
			//  Children.Add(value);
			//}
		}

		protected override void OnPaint(PaintEventArgs e)
		{
			//     base.OnPaint(e);
		}

		protected override void OnPaintBackground(PaintEventArgs pevent)
		{
			//    base.OnPaintBackground(pevent);
		}

		protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
		{
			e.DrawBackground();

			try
			{
				float stringWidth = e.Graphics.MeasureString(e.Header.Text, this.Font).Width;
				Point l = e.Bounds.Location;
				l.X = e.Bounds.Location.X; //+= (int)stringWidth + 10;
				if (e.Header.ImageKey != string.Empty)
					e.Graphics.DrawImage(this.SmallImageList.Images[e.Header.ImageKey], l);
			}
			catch
			{
			}
			e.DrawText();
			//     base.OnDrawColumnHeader(e);
		}


		private Brush highlightBrush = Brushes.CornflowerBlue;
		private Brush windowBrush = Brushes.White;
		private Pen controldarkPen = new Pen(SystemColors.ControlDark);

		private static string last;
		private Brush lastBrush = Brushes.Tomato;
		private Rectangle lastRectangle = new Rectangle(0,0,0,0);

		protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
		{
				Graphics g = e.Graphics;
				ExtendedListViewItem elvi = null;
				Rectangle r = e.Bounds;
				Font f = SystemFonts.DefaultFont;
				Brush b;

				bool hasDrawnText = false;

				if (e.Item is ExtendedListViewItem)
				{
					elvi = e.Item as ExtendedListViewItem;

					if (elvi.VirtualItem!=null && elvi.VirtualItem.GroupHeaderIdx == -2)
					{
						if (e.SubItem.Text != (e.Item.SubItems[0].Text))
							return;

						f = new Font(FontFamily.GenericSerif, 9, FontStyle.Bold);
						TextFormatFlags tffh = TextFormatFlags.Left;
						r.Width = this.Width;
						r.Y += 4;
						g.SetClip(r);
						Rectangle r2 = r;
						r2.Height -= 4;
						g.FillRectangle(windowBrush, r2);
						TextRenderer.DrawText(g, e.SubItem.Text, f, r, e.Item.ForeColor, tffh);
						return;
					}

					if (elvi.VirtualItem != null &&  elvi.VirtualItem.GroupHeaderIdx == -1)
					{
						if (e.SubItem.Text != (e.Item.SubItems[0].Text))
							return;
						f = new Font(f, FontStyle.Bold);
						r.Width = this.Width;
						Brush gb = new System.Drawing.Drawing2D.LinearGradientBrush(new Rectangle(0, 0, 250, 3), Color.DarkBlue, Color.LightBlue, 0f);
						g.DrawLine(new Pen(gb), new Point(e.Bounds.X + 2, e.Bounds.Y + 3), new Point(e.Bounds.X + 250, e.Bounds.Y + 3));
						return;
					}
				}



				DepthOffset = (viewMode == ExtendedListViewMode.List) ? 0 : 10;
				int checkBoxOffset = 15;
				int secondImageOffset = base.CheckBoxes ? checkBoxOffset : 0;
				int imageOffset = SecondaryImageList != null ? secondImageOffset + 13 : secondImageOffset;
				int textOffset = SmallImageList == null ? imageOffset : imageOffset + 18;

				r.Height -= 1;
				Rectangle cr = r;

				int depth = 0;

				Pen p = controldarkPen;

				object x = e.SubItem;
				object y = e.Item.SubItems[0];

				if (e.SubItem.Text == (e.Item.SubItems[0].Text))
				{

					cr.Y -= 3;
					cr.Height += 3;
					g.SetClip(cr);
					r.X += DepthOffset + 3;
					r.Width = Math.Max(0, r.Width - DepthOffset + 3);
					if (e.Item is ExtendedListViewItem)
					{
						elvi = e.Item as ExtendedListViewItem;


						int key = ((elvi.IsExpanded) ? 1 : 0) + ((elvi.Checked) ? 2 : 0) + ((elvi.Selected) ? 4 : 0);;

						if (elvi.VirtualItem != null && !elvi.VirtualItem.IsRedrawRequired
							&& elvi.VirtualItem.key == key
							&& elvi.VirtualItem.Bitmaps.ContainsKey(0)
						  && elvi.VirtualItem.Bitmaps[0].Width == e.Bounds.Width)
						{
							Bitmap bm = elvi.VirtualItem.Bitmaps[0];
							cr = e.Bounds;

							cr.Height += 1;
							g.SetClip(cr);

							g.DrawImage(bm, new Point(e.Bounds.X, e.Bounds.Y));
							r.X = bm.Width;
							r.Width -= bm.Width;
							r.Width += 13;

							hasDrawnText = true;
						}
						else
						{
							int yOffset = 0;
							int xOffset = 0;
							Bitmap bm = new Bitmap(e.Bounds.Width, e.Bounds.Height+1);
							g = Graphics.FromImage(bm);
							if (e.Item.Selected)
							{
								b = Brushes.LightBlue;
							}
							else
							{
								b = windowBrush ;
							}
							Rectangle fillRect = r;
							fillRect.Height += 1;
							fillRect.Width += 7;
							g.FillRectangle(b, new Rectangle(new Point(0, 0), new Size(fillRect.Width, fillRect.Height)));

							depth = elvi.Depth;
							r.X += depth * DepthOffset;
							r.Width = Math.Max(0, r.Width - depth * DepthOffset);

							p.DashStyle = DashStyle.Dot;
							if (depth > 0 && viewMode == ExtendedListViewMode.Tree)
							{
								for (int i = 1; i < depth; i++)
								{
									if (!GetAncestor(elvi.VirtualItem, depth - i).IsLastChild)
									{
										g.DrawLine(p, new Point(xOffset + 5 + i * DepthOffset - DepthOffset, yOffset - 5),
															 new Point(xOffset + 5 + i * DepthOffset - DepthOffset, yOffset + 18));
									}
								}
								g.DrawLines(p, new Point[] {new Point(xOffset + 5 + depth*DepthOffset-DepthOffset,yOffset-4),
							                                                              new Point(xOffset + 5 + depth*DepthOffset-DepthOffset,yOffset+6),
							                                                              new Point(xOffset + 5 + depth*DepthOffset,yOffset+6) });
								if (!elvi.IsLastChild)
								{
									g.DrawLine(p, new Point(xOffset + 5 + depth * DepthOffset - DepthOffset, yOffset + 6),
														 new Point(xOffset + 5 + depth * DepthOffset - DepthOffset, yOffset + 18));
								}
							}
							if ((elvi.Children.Count > 0 || elvi.ChildCount > 0) && viewMode == ExtendedListViewMode.Tree)
							{
								g.DrawImage(imageList1.Images[elvi.IsExpanded ? 1 : 0], new Rectangle(xOffset + 2 + depth * DepthOffset, yOffset + 2, 9, 9));
								if (elvi.IsExpanded)
								{
									g.DrawLine(p, new Point(xOffset + 5 + depth * DepthOffset, yOffset + 11),
																new Point(xOffset + 5 + depth * DepthOffset, yOffset + 16));

								}

								//g.DrawLine(p6, new Point(xOffset + 11 + depth * DepthOffset, yOffset + 6),
								//              new Point(xOffset + 17 + depth * DepthOffset, yOffset + 6));
							}
							else if (viewMode == ExtendedListViewMode.Tree)
							{
								g.DrawLine(p, new Point(xOffset + 3 + depth * DepthOffset, yOffset + 6),
															new Point(xOffset + 17 + depth * DepthOffset, yOffset + 6));
							}


							if (base.CheckBoxes && elvi != null && elvi.ChildCount == 0 && elvi.HasCheckbox)
							{
								g.DrawImage(imageList1.Images[elvi.Checked ? 2 : 3], new Rectangle(xOffset + 16 + depth * DepthOffset, yOffset, 16, 16));
							}
							else if (base.CheckBoxes && elvi.ChildCount > 0 && elvi.HasCheckbox)
								g.DrawImage(imageList1.Images[((int) elvi.VirtualItem.GetFolderCheckState())+4], new Rectangle(xOffset + 16 + depth * DepthOffset, yOffset, 16, 16));

							else
							{
								secondImageOffset -= checkBoxOffset;
								imageOffset -= checkBoxOffset;
								textOffset -= checkBoxOffset;
								//g.DrawLine(p, new Point(xOffset + 14 + depth * DepthOffset, yOffset + 6),
								//              new Point(xOffset + 29 + depth * DepthOffset, yOffset + 6));
							}
							if (secondaryImageList != null)
							{
								try
								{
									Image i = null;

									if (elvi.SecondaryImageIndex != -1)
										i = secondaryImageList.Images[elvi.SecondaryImageIndex];
									if (elvi.SecondaryImageKey != null)
										i = secondaryImageList.Images[elvi.SecondaryImageKey];
									if (i != null)
									{
										if (i is Bitmap)
											(i as Bitmap).MakeTransparent(Color.White);
										g.DrawImage(i, new Rectangle(xOffset + 14 + depth * DepthOffset + secondImageOffset, yOffset, 16, 16));
									}
									else
									{
										imageOffset -= 13;
										textOffset -= 13;
									}

									//g.DrawLine(p, new Point(xOffset + 17 + depth * DepthOffset + secondImageOffset, yOffset + 6),
									//              new Point(xOffset + 28 + depth * DepthOffset + secondImageOffset, yOffset + 6));

								}
								catch
								{
								}
							}

							if (e.Item.ImageKey != string.Empty && SmallImageList != null)
							{
								Image i = SmallImageList.Images[e.Item.ImageKey];
								g.DrawImageUnscaled(i, new Rectangle(xOffset + 17 + depth * DepthOffset + imageOffset, yOffset, 16, 16));
							}
							r.X += textOffset + (viewMode == ExtendedListViewMode.List ? 13 : 0);
							r.Width = Math.Max(0, r.Width - textOffset);

							TextFormatFlags tffm = TextFormatFlags.Left | TextFormatFlags.WordEllipsis;
							Rectangle textRect = r;
							textRect.Y = 0;
							textRect.Height = 15;
//							g.FillRectangle(b, textRect);
//							g.Flush();
//							g.Save();
							TextRenderer.DrawText(g, e.SubItem.Text, f, textRect, elvi.ForeColor, (e.Item.Selected) ? Color.LightBlue : Color.White, tffm);
						//	g.DrawString(e.SubItem.Text, f, Brushes.Red, (float)textRect.X, 0f);

							hasDrawnText = true;

							//Bitmap bm2 = new Bitmap(r.Width, r.Height);
							//Graphics g2 = Graphics.FromImage(bm2);
							//g2.DrawImageUnscaledAndClipped(bm, new Rectangle(0, 0, r.X, r.Height));
							if (elvi.VirtualItem != null)
							{
								elvi.VirtualItem.Bitmaps[0] = bm;
								elvi.VirtualItem.IsRedrawRequired = false;
								elvi.VirtualItem.key = key;
							}
							g = e.Graphics;

							cr = e.Bounds;
							
							cr.Height += 1;
							g.SetClip(cr);

							g.DrawImageUnscaled(bm, new Point(e.Bounds.X, e.Bounds.Y));
							last = elvi.Text;

						}
					}
				}
				else if (e.SubItem is ListViewItem.ListViewSubItem)
				{
					ExtendedListViewImageSubItem elvisi = e.SubItem as ExtendedListViewImageSubItem;
					if (e.Item.Selected)
					{
						b = Brushes.LightBlue;
					}
					else
					{
						b = windowBrush;
					}
					g.FillRectangle(b, new Rectangle(e.Bounds.Location, e.Bounds.Size));
				}

				else if (e.SubItem is ExtendedListViewImageSubItem)
				{
					ExtendedListViewImageSubItem elvisi = e.SubItem as ExtendedListViewImageSubItem;
					if (e.Item.Selected)
					{
						b = highlightBrush;
					}
					else
					{
						b = windowBrush;
					}
					g.FillRectangle(b, new Rectangle(e.Bounds.Location, e.Bounds.Size));
					if (elvisi != null && secondaryImageList.Images.ContainsKey(elvisi.ImageKey))
					{

						Image i = secondaryImageList.Images[elvisi.ImageKey];
						g.DrawImage(i, new Rectangle(e.Bounds.X, e.Bounds.Y, 14, 14));
						r.Width = r.Width - 16;
						r.X = r.X + 16;
					}
				}

				if (!hasDrawnText)
				{
					TextFormatFlags tff = TextFormatFlags.Left | TextFormatFlags.WordEllipsis;
					TextRenderer.DrawText(g, e.SubItem.Text, f, r, e.Item.ForeColor, tff);
				}

				ExtendedListViewItem elvi2 = e.Item as ExtendedListViewItem;

				//if (e.Item.Selected && e.SubItem.Text == (e.Item.SubItems[0].Text)) // && !elvi2.VirtualItem.hasDropRect)
				//{
					//r = e.Item.Bounds;
					//Console.WriteLine("Drawing focus rect");
					//e.DrawFocusRectangle(r);//e.Item.Bounds);
					//e.DrawFocusRectangle(r);//e.Item.Bounds);
					//  elvi2.VirtualItem.hasDropRect = true;
				//}
					//e.Graphics.Flush();
					//e.DrawFocusRectangle(e.Item.Bounds);
		}

		private MouseButtons lastButton = MouseButtons.None;

		protected override void OnMouseDown(MouseEventArgs e)
		{
			base.OnMouseDown(e);
			lastButton = e.Button;
		}
		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.Click"></see> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
		protected override void OnClick(EventArgs e)
		{
			if (lastButton == MouseButtons.Left)
			{
				int depthOffset = (viewMode == ExtendedListViewMode.List) ? 0 : 10;
				Point hit = PointToClient(MousePosition);
				ListViewHitTestInfo hti = base.HitTest(hit);
				BeginUpdate();
				if (hti.Item is ExtendedListViewItem)
				{
					ExtendedListViewItem elvi = hti.Item as ExtendedListViewItem;
					if (elvi.ChildCount == 0 && elvi.HasCheckbox && hit.X > (16 + elvi.Depth * depthOffset) && hit.X < (32 + elvi.Depth * depthOffset))
					{
						ChildCheckStateRecalcGuid = Guid.NewGuid();
						elvi.VirtualItem.Checked = !elvi.VirtualItem.Checked;
						elvi.VirtualItem.IsRedrawRequired = true;
						this.RedrawItems(0,VirtualListSize-1,false);

						//if (elvi.ParentItem != null)
						//  elvi.ParentItem.IsRedrawRequired = true;
					}
					else if (elvi.ChildCount > 0 && hit.X > (16 + elvi.Depth * depthOffset) && hit.X < (32 + elvi.Depth * depthOffset))
					{
						//foreach (ExtendedVirtualViewItem evvi in elvi.VirtualItem.AllDescendants)
						//  evvi.Checked = !elvi.Checked;
						//elvi.Checked = !elvi.Checked;
						elvi.VirtualItem.IsRedrawRequired = true;
						tmsThisItem.Visible = elvi.HasCheckbox;
						cmsCheckOptions.Tag = elvi.VirtualItem;
						cmsCheckOptions.Show(this, hit);
					}
					if (hit.X > elvi.Depth * DepthOffset && hit.X < elvi.Depth * DepthOffset + 15)
					{
						if (elvi.IsExpanded)
						{
							if (base.VirtualMode)
							{
								if (DoHideChildren != null)
									DoHideChildren(this, new ExtendedListViewItemEventArgs(elvi));
							}
						}
						else
						{
							if (base.VirtualMode)
							{
								if (DoShowChildren != null)
									DoShowChildren(this, new ExtendedListViewItemEventArgs(elvi));
							}
						}
					}
				}
				EndUpdate();
			}
			base.OnClick(e);
		}

		private ExtendedVirtualViewItem GetAncestor(ExtendedVirtualViewItem item, int level)
		{
			for (int i = 0; i < level; i++)
				if (item.ParentItem != null)
					item = item.ParentItem;
			return item;
		}

		private void CheckContextMenu_Click(object sender, EventArgs e)
		{
			ExtendedVirtualViewItem evvi = cmsCheckOptions.Tag as ExtendedVirtualViewItem;
			ChildCheckStateRecalcGuid = Guid.NewGuid();
			if (sender == tmsThisItem)
			{
				evvi.Checked = !evvi.Checked;
				evvi.IsRedrawRequired = true;

			}
			else if (sender == tmsThisFolderAndChildren)
			{
				foreach (ExtendedVirtualViewItem child in evvi.Children)
				{
					if (child.HasCheckbox)
					{
						child.Checked = !evvi.Checked;
						child.IsRedrawRequired = true;
					}
				}
				evvi.Checked = !evvi.Checked;
				evvi.IsRedrawRequired = true;
			}
			else if (sender == tmsThisFolderAndAllDescendants)
			{
				foreach (ExtendedVirtualViewItem child in evvi.AllDescendants)
				{
					child.Checked = !evvi.Checked;
					child.IsRedrawRequired = true;
				}
				evvi.Checked = !evvi.Checked;
				evvi.IsRedrawRequired = true;
				//if (evvi.ParentItem != null)
				//  evvi.ParentItem.IsRedrawRequired = true;
			}
			this.RedrawItems(0, VirtualListSize - 1, false);
		}




		private int IndexComparision(ListViewItem x, ListViewItem y)
		{
			return (x.Index == y.Index) ? 0 : (x.Index < y.Index) ? -1 : 1;
		}

		internal void ClearInternal()
		{
			base.Items.Clear();
		}

		internal void RemoveInternal(ListViewItem item)
		{
			base.Items.Remove(item);
		}


		public class ExtendedListViewItemCollection : List<ExtendedListViewItem>
		{
			ExtendedListViewItem _parent;
			ExtendedListView _owner;
			public ExtendedListViewItemCollection(ExtendedListView owner, ExtendedListViewItem parent)
				: base()
			{
				_parent = parent;
				_owner = owner;
			}

			public ExtendedListViewItemCollection(ExtendedListView owner)
				: base()
			{
				_parent = null;
				_owner = owner;
			}


			public ExtendedListViewItem Last
			{
				get
				{
					if (base.Count == 0)
						return null;
					return base[base.Count - 1];
				}
			}

			public ExtendedListViewItem First
			{
				get
				{
					if (base.Count == 0)
						return null;
					return base[0];
				}
			}

			public new void Remove(ExtendedListViewItem item)
			{
				base.Remove(item);
				_owner.RemoveInternal(item);
			}

			public new void Clear()
			{
				base.Clear();
				_owner.ClearInternal();
			}

			
		}

		public class ExtendedVirtualViewItemCollection : List<ExtendedVirtualViewItem>
		{
			ExtendedVirtualViewItem _parent;
			ExtendedListView _owner;
			public ExtendedVirtualViewItemCollection(ExtendedListView owner, ExtendedVirtualViewItem parent)
				: base()
			{
				_parent = parent;
				_owner = owner;
			}

			public ExtendedVirtualViewItemCollection(ExtendedListView owner)
				: base()
			{
				_parent = null;
				_owner = owner;
			}


			public ExtendedVirtualViewItem Last
			{
				get
				{
					if (base.Count == 0)
						return null;
					return base[base.Count - 1];
				}
			}

			public ExtendedVirtualViewItem First
			{
				get
				{
					if (base.Count == 0)
						return null;
					return base[0];
				}
			}
		}

		
	}

	public class ExtendedListViewItemEventArgs : EventArgs
	{
		public ExtendedListViewItem ExtendedListViewItem;

		public ExtendedListViewItemEventArgs(ExtendedListViewItem extendedListViewItem)
		{
			this.ExtendedListViewItem = extendedListViewItem;
		}
	}

	public enum FolderCheckState
	{
		Checked,
		Unchecked,
		PartiallyChecked
	}
}